﻿using Newtonsoft.Json;
using SimpleRPC.Handler;
using SimpleRPC.Helper;
using SimpleRPC.Message;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace SimpleRPC
{
    public class RpcServer
    {
        public RpcServer(int port)
        {
            _port = port;
            _server = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            _lengthFieldEncoder = new LengthFieldEncoder();
        }
        private bool _listen;
        private ConcurrentDictionary<String, Socket> _clients = new ConcurrentDictionary<string, Socket>();
        private LengthFieldEncoder _lengthFieldEncoder;

        private void AcceptAsync()
        {
            Task.Factory.StartNew(() =>
            {
                while (_listen)
                {
                    var client = _server.Accept();
                    client.ReceiveTimeout = 5000;
                    var key = client.RemoteEndPoint.ToString();
                    _clients[key] = client;

                    Task.Factory.StartNew(() =>
                    {
                        ServerReceiveHandler(client);
                    });
                }
            });
        }

        private void ServerReceiveHandler(Socket client)
        {
            int len = 0;
            List<byte> buffer = new List<byte>(512 * 1024);
            while (true)
            {
                byte[] buf = new byte[4 * 1024];
                try
                {
                    len = client.Receive(buf);
                }
                catch
                {
                    break;
                }
                if (len > 0)
                {
                    if (len == buf.Length)
                    {
                        buffer.AddRange(buf);
                    }
                    else
                    {
                        buffer.AddRange(buf.Take(len));
                    }
                    // 前两个字节为长度
                    var frameLen = BitConverter.ToInt16(buffer.Take(2).ToArray(), 0);
                    if (buffer.Count >= frameLen + 2)
                    {
                        var frame = buffer.Skip(2).Take(frameLen).ToArray();
                        buffer.RemoveRange(0, frameLen + 2);

                        var response = this.GetResponse(JsonConvert.DeserializeObject<Request>(Encoding.UTF8.GetString(frame)));
                        if (response != null)
                        {
                            var resBuffer = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(response));
                            _lengthFieldEncoder.Encode(resBuffer, out byte[] sendMsg);
                            int sendLen = client.Send(sendMsg);
                        }
                    }
                }
                else
                {
                    break;
                }
            }
            var key = client.RemoteEndPoint.ToString();
            var removed = _clients.TryRemove(key, out Socket value);
            client.Close();
        }

        #region 私有成员

        private int _port { get; set; }
        private Socket _server { get; }
        private Dictionary<string, Type> _serviceHandle { get; set; } = new Dictionary<string, Type>();
        internal Response GetResponse(Request request)
        {
            Response response = new Response();

            try
            {
                var requestModel = request;
                if (!_serviceHandle.ContainsKey(requestModel.ServiceName))
                    throw new Exception("未找到该服务");
                var serviceType = _serviceHandle[requestModel.ServiceName];
                var service = Activator.CreateInstance(serviceType);
                var method = serviceType.GetMethod(requestModel.MethodName);
                if (method == null)
                    throw new Exception("未找到该方法");
                var paramters = requestModel.Paramters.ToArray();
                var methodParamters = method.GetParameters();
                for (int i = 0; i < methodParamters.Length; i++)
                {
                    if (paramters[i].GetType() != methodParamters[i].ParameterType)
                    {
                        paramters[i] = JsonConvert.DeserializeObject(JsonConvert.SerializeObject(paramters[i]), methodParamters[i].ParameterType);
                    }
                }
                var res = method.Invoke(service, paramters);

                response.Success = true;
                response.Data = JsonConvert.SerializeObject(res);
                response.Message = "请求成功";
            }
            catch (Exception ex)
            {
                response.Success = false;
                response.Message = ExceptionHelper.GetExceptionAllMsg(ex);
            }

            return response;
        }

        #endregion

        #region 外部接口

        /// <summary>
        /// 注册服务
        /// 注：默认注册的服务为接口名
        /// </summary>
        /// <typeparam name="IService">服务接口</typeparam>
        /// <typeparam name="Service">服务实现</typeparam>
        public void RegisterService<IService, Service>() where Service : class, IService where IService : class
        {
            RegisterService<IService, Service>(typeof(IService).Name);
        }

        /// <summary>
        /// 注册服务,指定服务名
        /// </summary>
        /// <typeparam name="IService">服务接口</typeparam>
        /// <typeparam name="Service">服务实现</typeparam>
        /// <param name="serviceName">服务名</param>
        public void RegisterService<IService, Service>(string serviceName) where Service : class, IService where IService : class
        {
            _serviceHandle.Add(serviceName, typeof(Service));
        }

        /// <summary>
        /// 开始运行服务
        /// </summary>
        public void Start()
        {
            _server.Bind(new IPEndPoint(IPAddress.Any, _port));
            _server.Listen(100);
            _listen = true;
            AcceptAsync();
        }

        /// <summary>
        /// 停止服务
        /// </summary>
        public void Stop()
        {
            _listen = false;
            _server.Close();
        }

        #endregion
    }
}
